﻿using System;
using System.IO;
using System.Linq;
using System.Net;
using Newtonsoft.Json;
using PTop.Application.Biz;
using PTop.Code;
using PTop.Code.Date;
using PTop.Domain.Entity.WeChat;
using PTop.Domain.ViewModel.Wechat.DTO;

namespace PTop.Application.WeChat
{
    public class WxApiApp
    {
        public readonly string APPID;
        public readonly string APPSECRET;
        public readonly string WECHAT_ACCESS_TOKEN_KEY = "access_token";
        public const string WECHAT_GETOPENID_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type=authorization_code";

        public WxApiApp()
        {
            APPID = Configs.GetValue("WeChatAppId");
            APPSECRET = Configs.GetValue("WeChatAppSecret");
        }

        public UserInfoDTO GetAndSaveUserInfo(string openId)
        {
            var token = GetAccessToken().access_token;
            var url = string.Format("https://api.weixin.qq.com/cgi-bin/user/info?access_token={0}&openid={1}&lang=zh_CN", token, openId);
            var dto = new UserInfoDTO();
            for (int i = 0; i < 3; i++)
            {
                //调用微信接口获取token
                var res = HttpGet(url);
                dto = JsonConvert.DeserializeObject<UserInfoDTO>(res);
                //判断token是否有效
                if (dto != null)
                {
                    try
                    {
                        var entity = new WxUserInfoEntity();
                        entity.F_Id = Guid.NewGuid().ToString();
                        entity.F_OpenId = dto.openid;
                        entity.F_NickName = dto.nickname;
                        entity.F_Sex = int.Parse(dto.sex);
                        entity.F_Language = dto.language;
                        entity.F_City = dto.city;
                        entity.F_Province = dto.province;
                        entity.F_Country = dto.country;
                        entity.F_HeadImgUrl = dto.headimgurl;
                        entity.F_SubscribeTime = DateHelper.TimestampToDateTime(long.Parse(dto.subscribe_time));
                        entity.F_Remark = dto.remark;
                        entity.F_GroupId = dto.groupid;
                        new WxUserInfoApp().SubmitForm(entity, null);
                        //跟新关注量统计
                        new UserStatisticsApp().AddCount("F_SubscribeCount");
                    }
                    catch (Exception ex)
                    {
                        LogFactory.GetLogger("Wechat").Error(ex.Message + ex.StackTrace);
                    }
                    return dto;
                }
                else
                {
                    LogFactory.GetLogger("Wechat").Error("获取微信账号失败：" + res);
                }
            }
            return dto;
        }

        public AccessTokenDTO GetAccessToken()
        {
            AccessTokenDTO dto = null;

            //1.从cache中读
            dto = CacheFactory.Cache().GetCache<AccessTokenDTO>(WECHAT_ACCESS_TOKEN_KEY);
            if (dto == null)
            {
                //LogNet.Error("access_token dto为空");
            }
            if (dto != null && dto.expireTime > DateTime.Now)
            {
                return dto;
            }

            //2.从库中取,并根据有效期判断是否需要重新生成
            var accessTokenApp = new WxAccessTokenApp();
            var entity = accessTokenApp.GetForm();

            //如果有效期>60分钟则保留,<60分钟就重新请求一个新的token
            if (entity != null && (entity.F_ExpireTime - DateTime.Now).TotalMinutes > 60)
            {
                dto = new AccessTokenDTO()
                {
                    access_token = entity.F_AccessToken,
                    expireTime = entity.F_ExpireTime,
                    expires_in = (int)(entity.F_ExpireTime - DateTime.Now).TotalSeconds
                };
                //缓存20分钟
                CacheFactory.Cache().WriteCache(dto, WECHAT_ACCESS_TOKEN_KEY, DateTime.Now.AddMinutes(20));
                return dto;
            }

            dto = ResetAccessToken();
            return dto;
        }

        public AccessTokenDTO ResetAccessToken()
        {
            //3.调用微信接口重新生成token
            var dto = GetAccessTokenPrivate();

            //4.缓存20分钟
            if (dto != null && !string.IsNullOrEmpty(dto.access_token))
            {
                CacheFactory.Cache().WriteCache(dto, WECHAT_ACCESS_TOKEN_KEY, DateTime.Now.AddMinutes(20));
            }

            return dto;
        }

        /// <summary>
        /// 使用code换取openID
        /// </summary>
        /// <param name="code">微信授权页面的code</param>
        /// <returns></returns>
        public string GetWeChatOpenId(string code)
        {
            string url = string.Format(WECHAT_GETOPENID_URL, APPID, APPSECRET, code);
            string json = HttpGet(url);
            char[] c = { ',' };
            return json.Split(c).Where(s => s.Contains("openid")).First().Substring(10).Substring(0, 28);
        }

        public string GetTiket()
        {
            var cacheKey = "WeChatJsApiTiket_" + APPID;
            var ticketJson = CacheFactory.Cache().GetCache<string>(cacheKey);
            if (ticketJson != null)
            {
                return ticketJson;
            }
            var tokenDto = GetAccessToken();
            var accessToken = tokenDto.access_token;
            var url = string.Format("https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token={0}", accessToken);
            var jsonStr = HttpGet(url, "");
            if (jsonStr.Contains("40001"))
            {
                tokenDto = ResetAccessToken();
                accessToken = tokenDto.access_token;
                url = string.Format("https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token={0}", accessToken);
                jsonStr = HttpGet(url, "");
                if (jsonStr.Contains("40001"))
                {
                    throw new Exception("无法获取有效的access_token");
                }
            }
            var qrcode = new
            {
                ticket = "",
                expire_seconds = "",
                errcode = "",
                errmsg = ""
            };
            var ticket = Json.DeserializeAnonymousType(jsonStr, qrcode);
            try
            {
                CacheFactory.Cache().WriteCache(ticket.ticket, cacheKey, DateTime.Now.AddHours(1));
            }
            catch
            {

            }
            return ticket.ticket;
        }


        private AccessTokenDTO GetAccessTokenPrivate()
        {
            var url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", APPID, APPSECRET);
            AccessTokenDTO dto = null;
            for (int i = 0; i < 3; i++)
            {
                //调用微信接口获取token
                var res = HttpGet(url);
                dto = JsonConvert.DeserializeObject<AccessTokenDTO>(res);
                //判断token是否有效
                if (dto != null && dto.errcode == null && !string.IsNullOrEmpty(dto.access_token))
                {
                    var entity = new WxAccessTokenEntity();
                    entity.F_Id = Guid.NewGuid().ToString();
                    entity.F_AccessToken = dto.access_token;
                    entity.F_CreatorTime = DateTime.Now;
                    entity.F_ExpireTime = dto.expireTime = DateTime.Now.AddSeconds(dto.expires_in).AddMinutes(-5);
                    var accessTokenApp = new WxAccessTokenApp();
                    accessTokenApp.SubmitForm(entity, null);

                    return dto;
                }
            }
            //LogNet.Error("GetAccessTokenPrivate()无法获取accesstoken： " + url);
            return null;
        }

        /// <summary>
        /// 同步方式发起HTTP GET 请求
        /// </summary>
        /// <param name="url">请求的地址</param>
        /// <param name="queryString">请求的参数</param>
        /// <returns></returns>
        protected string HttpGet(string url, string queryString = null)
        {
            string responseData = string.Empty;
            //LogNet.Info(string.Format("外部接口调用 url:{0} data:{1}", url, queryString));

            try
            {
                if (!string.IsNullOrEmpty(queryString))
                {
                    url += "?" + queryString;
                }

                HttpWebRequest webRequest = WebRequest.Create(url) as HttpWebRequest;
                webRequest.Method = "GET";
                webRequest.ServicePoint.Expect100Continue = false;
                webRequest.Timeout = 20000;

                StreamReader responseReader = null;
                try
                {
                    responseReader = new StreamReader(webRequest.GetResponse().GetResponseStream());
                    responseData = responseReader.ReadToEnd();
                }
                catch (Exception ex)
                {
                    //LogNet.Error(string.Format("外部接口调用 url:{0} data:{1}", url, queryString), ex);
                }
                finally
                {
                    webRequest.GetResponse().GetResponseStream().Close();
                    responseReader.Close();
                    responseReader = null;
                    webRequest = null;
                }
            }
            catch (Exception exp)
            {
                //Error(monitorID);
                //LogNet.Error(string.Format("外部接口调用 url:{0} data:{1}", url, queryString), exp);
            }

            return responseData;
        }

    }
}